
// 服务ID
var SERVICE_UUID = '000018F0-0000-1000-8000-00805F9B34FB';
// 写入特性ID
var WRITE_UUID = '00002AF1-0000-1000-8000-00805F9B34FB';
// 通知特性ID
var NOTIFY_UUID = '00002AF0-0000-1000-8000-00805F9B34FB';

/**
 * 蓝牙对象
 */
var BLEUtils = {
  /**
   * 设备ID
   */
  deviceId:null,
  /**
   * 数据接收
   * @param {*} value 二进制数据
   */
  onRecevice: function (value) {
    //获取接收的数据，转换为8位无符号整型数组
    var data = new Uint8Array(value);
    // var hexString = this.bytes2HexString(data);
    // console.log('接收',hexString);
    //初步过滤数据
    if (data.length < 5) {
      throw Error('非法数据！');
    }
    //比对校验和
    var cs = protocol.getCSData(data);
    if (cs == data[data.length - 1]) {
      var frameType = (data[2] & 0xff);
      var eventType = null;
      var dataResult = new dataResultInfo();
      dataResult.result = (data[3] & 0xff) == 0x00 ? true : false;
      switch (frameType) {
        case EFrameType.HANDSHAKE:
          eventType = EEventType.onHandshake;
          dataResult.data = { version: "", count: 0 };
          dataResult.data.version = (data[4] & 0xff) + "." + (data[5] & 0xff);//版本号
          dataResult.data.count = (data[6] & 0xff) + ((data[7] & 0xff) << 8);//未上传的盖章记录数量
          break;
      case EFrameType.BATTERY:
        eventType = EEventType.onBattery;
        dataResult.data = { battery: 0 };
        dataResult.data.battery = (data[3] & 0xff);//电量
        dataResult.result = true;
        break;
        case EFrameType.START:
          eventType = EEventType.onStart;
          if (dataResult.result) {
            //启动序号
            dataResult.data = { startNo: 0 };
            dataResult.data.startNo = (data[4] & 0xff) + ((data[5] & 0xff) << 8) + ((data[6] & 0xff) << 16) + ((data[7] & 0xff) << 24);
          }
          break;
        case EFrameType.GET_PRESS_DURATION:
          eventType = EEventType.onPressDuration;
          dataResult.data = { duration: 0 };
          dataResult.data.duration = (data[3] & 0xff);//按压时长
          dataResult.result = true;
          break;
      case EFrameType.SET_PRESS_DURATION:
        eventType = EEventType.onPressDuration;
      break;
        case EFrameType.GET_TIMEOUT:
          eventType = EEventType.onTimeout;
          dataResult.data = { timeout: 0 };
          dataResult.data.timeout = (data[3] & 0xff) * 30;//盖章超时时长
          dataResult.result = true;
          break;
        case EFrameType.SET_TIMEOUT:
          eventType = EEventType.onTimeout;
        break;
        case EFrameType.SYNC_RECORD:
          // eventType = EEventType.onSyncRecord;
          // dataResult.result = true;
          // dataResult.data = { count : 0 };
          // // 待上传盖章次数
          // dataResult.data.count = (data[3] & 0xff) + ((data[4] & 0xff) << 8);
          break;
        case EFrameType.LOCK:
          eventType = EEventType.onLock;
          break;
        case EFrameType.UPLOAD_STAMP:
          eventType = EEventType.onStamp;
          dataResult.result = true;
          dataResult.data = { startNo: 0, stampNo: 0, stampTime: "" };
          //启动序号
          // dataResult.data.startNo = (data[3] & 0xff) + ((data[4] & 0xff) << 8) + ((data[5] & 0xff) << 16) + ((data[6] & 0xff) << 24);
          //盖章序号
          dataResult.data.stampNo = (data[3] & 0xff) + ((data[4] & 0xff) << 8)
          //盖章时间
          dataResult.data.stampTime = ((data[5] & 0xff) + 2000) + "-" + (data[6] & 0xff) + "-" + (data[7] & 0xff)
            + " " + (data[8] & 0xff) + ":" + (data[9] & 0xff) + ":" + (data[10] & 0xff);
          //原路返回
          this.writeData2(data);
          break;
        case EFrameType.ALARM:
          eventType = EEventType.onAlarm;
          dataResult.result = true;
          dataResult.data = { alarm: 0, alarmTime: "" };
          // 报警类型
          dataResult.data.alarm = (data[3] & 0xff);
          //报警时间
          dataResult.data.alarmTime = ((data[4] & 0xff) + 2000) + "-" + (data[5] & 0xff) + "-" + (data[6] & 0xff)
            + " " + (data[7] & 0xff) + ":" + (data[8] & 0xff) + ":" + (data[9] & 0xff);
          break;
      }
      dataResult.eventType = eventType;
      return dataResult;
    } else {
      throw Error('数据校验失败！');
    }
  },
  /**
   * 数据发送
   * @param {*} deviceId 设备ID
   * @param {*} value 二进制数据
   */
  writeData: function (deviceId, value) {
    var buffer = new ArrayBuffer(value.length);
    var dv = new DataView(buffer);
    for (var i = 0; i < value.length; i++) {
      dv.setUint8(i, value[i]);
    }
    // console.log('发送',this.bytes2HexString(value));
    wx.writeBLECharacteristicValue({
      characteristicId: WRITE_UUID,
      deviceId: deviceId,
      serviceId: SERVICE_UUID,
      value: buffer,
      fail: function () {

      }
    })
  },
  /**
   * 数据发送
   * @param {*} value 二进制数据
   */
  writeData2: function (value) {
    var buffer = new ArrayBuffer(value.length);
    var dv = new DataView(buffer);
    for (var i = 0; i < value.length; i++) {
      dv.setUint8(i, value[i]);
    }
    // console.log('发送',this.bytes2HexString(value));
    var _this = this;
    wx.writeBLECharacteristicValue({
      characteristicId: WRITE_UUID,
      deviceId: _this.deviceId,
      serviceId: SERVICE_UUID,
      value: buffer,
      fail: function () {

      }
    })
  },
  /**
   * 将字节数组转换为16进制字符串
   * @param {*} arrBytes 字节数组
   */
  bytes2HexString: function (arrBytes) {
    var str = "";
    for (var i = 0; i < arrBytes.length; i++) {
      var tmp;
      var num = arrBytes[i];
      if (num < 0) {
        //此处填坑，当byte因为符合位导致数值为负时候，需要对数据进行处理
        tmp = (255 + num + 1).toString(16);
      } else {
        tmp = num.toString(16);
      }
      if (tmp.length == 1) {
        tmp = "0" + tmp;
      }
      str += tmp + " ";
    }
    return str.toUpperCase();
  },
  /**
   * 将16进制字符串转换为字节数组
   * @param {*} hexString 16进制字符串
   */
  hexString2Bytes(hexString) {
    var pos = 0;
    var len = hexString.length;
    if (len % 2 != 0) {
      return null;
    }
    len /= 2;
    var hexA = new Array();
    for (var i = 0; i < len; i++) {
      var s = hexString.substr(pos, 2);
      var v = parseInt(s, 16);
      hexA.push(v);
      pos += 2;
    }
    return hexA;
  },
  /**
   * 将时间转换为字节数组
   * @param {*} time 
   */
  time2Bytes: function (time) {
    var array = new Array(6);
    if (time) {
      array[0] = time.getFullYear() - 2000;
      array[1] = time.getMonth() + 1;
      array[2] = time.getDate();
      array[3] = time.getHours();
      array[4] = time.getMinutes();
      array[5] = time.getSeconds();
    } else {
      for (var i = 0; i < 6; i++) {
        array[i] = 0;
      }
    }

    return array;
  },
  /**
   * 从广播包中获取MAC地址
   * @param {*} advertisData 
   */
  getMacFromAdvertisData: function (advertisData) {
    var hexArr = Array.prototype.map.call(
      new Uint8Array(advertisData),
      function (bit) {
        return ('00' + bit.toString(16)).slice(-2)
      }
    );
    return hexArr.reverse().join(':').substring(0, 17).toUpperCase();
  },
}
/**
 * 协议对象
 */
var protocol = {
  frameHead: 0xFF,//帧头
  dataLen: 0x00,//数据长度
  frameType: 0x00,//帧类型
  data: null,//数据域
  cs: 0x00,//校验和
  //计算校验和
  getCSData: function (data) {
    var cs = 0;
    for (var i = 0; i < data.length - 1; i++) {
      cs += data[i];
    }
    cs = cs % 256;

    return cs;
  },
  //获取协议数据
  getData() {
    //计算数据长度
    this.dataLen = this.data.length;
    //拼接数据
    var arrData = new Array(this.dataLen + 4);
    arrData[0] = this.frameHead;
    arrData[1] = this.dataLen;
    arrData[2] = this.frameType;
    for (var i = 0; i < this.data.length; i++) {
      arrData[i + 3] = this.data[i];
    }
    //计算校验和
    var cs = this.getCSData(arrData);
    arrData[arrData.length - 1] = cs;

    return arrData;
  },
  /**
   * 握手
   */
  getHandshake(){
    this.frameType = EFrameType.HANDSHAKE;
    this.data = new Array(6);
    var time = new Date()
    var timeArray = BLEUtils.time2Bytes(time);
    for (var i = 0; i < timeArray.length; i++) {
      this.data[i] = timeArray[i];
    }
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 查询电量
   */
  getBattery(){
    this.frameType = EFrameType.BATTERY;
    this.data = new Array(1);
    this.data[0] = 0;
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 启动印章
   * @param {*} count 
   * @param {*} time 
   */
  getStart: function (count, time) {
    this.frameType = EFrameType.START;
    this.data = new Array(8);
    this.data[0] = (count & 0xff);
    this.data[1] = ((count >> 8) & 0xff);
    var timeArray = BLEUtils.time2Bytes(time);
    for (var i = 0; i < timeArray.length; i++) {
      this.data[i + 2] = timeArray[i];
    }
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 查询/设置长按时间
   * @param {*} type 
   * @param {*} duration 
   */
  getPressDuration: function (type, duration) {
    this.frameType = EFrameType.SET_PRESS_DURATION;
    if(type==0){
      this.frameType = EFrameType.GET_PRESS_DURATION;
    }
    this.data = new Array(1);
    this.data[0] = (duration & 0xff);
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 查询/设置盖章超时时间
   * @param {*} type 
   * @param {*} timeout 
   */
  getTimeout: function (type, timeout) {
    this.frameType = EFrameType.SET_TIMEOUT;
    if(type==0){
      this.frameType = EFrameType.GET_TIMEOUT;
    }
    this.data = new Array(1);
    this.data[0] = (timeout & 0xff);
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 同步盖章动作
   */
  getSyncRecord: function () {
    this.frameType = EFrameType.SYNC_RECORD;
    this.data = new Array(0);
    this.data[0] = 0;
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 强制锁定印章
   */
  getLock: function () {
    this.frameType = EFrameType.LOCK;
    this.data = new Array(1);
    this.data[0] = 0;
    var buffer = this.getData();
    return buffer;
  },
  /**
   * 重置设备
   */
  getReset:function(){
    this.frameType = EFrameType.RESET;
    this.data = new Array(1);
    this.data[0] = 0;
    var buffer = this.getData();
    return buffer;
  }
}
/**
 * 数据帧类型枚举
 */
var EFrameType = {
  /**
   * 握手
   */
  HANDSHAKE:0xA0,
  /**
   * 电量
   */
  BATTERY:0xAF,
  /**
   * 启动印章
   */
  START: 0xA1,
  /**
   * 设置长按报警时间
   */
  SET_PRESS_DURATION: 0xA6,
  /**
   * 查询长按报警时间
   */
  GET_PRESS_DURATION: 0xA7,
  /**
   * 设置盖章超时时间
   */
  SET_TIMEOUT: 0xB4,
  /**
   * 查询盖章超时时间
   */
  GET_TIMEOUT: 0xB3,
  /**
   * 同步盖章动作
   */
  SYNC_RECORD:0xA3,
  /**
   * 强制锁定印章
   */
  LOCK:0xA9,
  /**
   * 盖章动作上传
   */
  UPLOAD_STAMP: 0xA2,
  /**
   * 报警上传
   */
  ALARM: 0xA8,
  /**
   * 重置设备
   */
  RESET: 0xA5
}
/**
 * 事件类型
 */
var EEventType = {
  /**
  * 发现设备事件
  */
  onDeviceFound: "onDeviceFound",
  /**
   * 连接设备事件
   */
  onConnectStatus: "onConnectStatus",
  /**
   * 握手事件
   */
  onHandshake:"onHandshake",
  /**
   * 电量事件
   */
  onBattery:"onBattery",
  /**
   * 启动印章事件
   */
  onStart: "onStart",
  /**
   * 查询/设置长按报警时间事件
   */
  onPressDuration: "onPressDuration",
  /**
   * 查询/设置盖章超时时间事件
   */
  onTimeout: "onTimeout",
  /**
   * 盖章动作事件
   */
  onStamp: "onStamp",
  /**
  * 报警事件
  */
  onAlarm: "onAlarm",
  /**
   * 重置设备
   */
  onReset:"onReset",
  /**
   * 同步盖章记录
   */
  onSyncRecord:"onSyncRecord",
  /**
   * 强制锁定印章
   */
  onLock:"onLock",
}
/**
 * 数据结果
 */
function dataResultInfo() {
  /**
   * 执行结果
   */
  this.result = true,
  /**
   * 事件类型
   */
  this.eventType = null;
  /**
   * 消息
   */
  this.message = "成功",
  /**
   * 数据对象
   */
  this.data = null
}

/**
 * 印章API类
 *
*/
class QSSmartSealWX {
  /**
     * 构造函数
     * 
     * @param {Object} options 接口参数,key 为必选参数
     */
  constructor(options) {
    // 对象实例
    this.instance = null;
    // 设备ID
    this.deviceId = null;
    // 协议版本号
    this.version = "2.0";

    // 发现设备事件
    this.onDeviceFound = null;
    // 连接设备事件
    this.onConnectStatus = null;

    // 握手事件
    this.onHandshake = null;
    // 电量事件
    this.onBattery = null;
    // 启动印章事件
    this.onStart = null;
    // 查询/设置长按报警时间事件
    this.onPressDuration = null;
    // 查询/设置盖章超时时间事件
    this.onTimeout = null;
    // 同步盖章动作事件
    this.onSyncRecord = null;

    // 盖章动作事件
    this.onStamp = null;
    // 报警事件
    this.onAlarm = null;

    // 重置设备
    this.onReset = null;

    // 强制锁定印章
    this.onLock = null;

    var _this = this;
    // 监听发现设备
    wx.onBluetoothDeviceFound((result) => {
      // 过滤设备
      for (var i = 0; i < result.devices.length; i++) {
        var device = result.devices[i];
        var localName = device.localName;
        if (localName == "BLE-BHQKL") {
          var deviceId = device.deviceId;
          var mac = BLEUtils.getMacFromAdvertisData(device.advertisData);
          if (_this.onDeviceFound) {
            // 回调
            var seal = { deviceId: deviceId, mac: mac };
            var dataResult = new dataResultInfo();
            dataResult.result = true;
            dataResult.eventType = EEventType.onDeviceFound;
            dataResult.data = seal;
            _this.onDeviceFound(dataResult);
          }
        }
      }
    })
    // 监听设备连接状态
    wx.onBLEConnectionStateChange(function (res) {
      if (_this.onConnectStatus) {
        var dataResult = new dataResultInfo();
        dataResult.result = true;
        dataResult.eventType = EEventType.onConnectStatus;
        dataResult.data = { connectStatus: res.connected };
        _this.onConnectStatus(dataResult);
      }
    });
    //监听数据接收
    wx.onBLECharacteristicValueChange((result) => {
      var dataResult = BLEUtils.onRecevice(result.value);
      switch (dataResult.eventType) {
        case EEventType.onHandshake:
          if (_this.onHandshake) {
            _this.onHandshake(dataResult);
          }
          break;
        case EEventType.onBattery:
          if (_this.onBattery) {
            _this.onBattery(dataResult);
          }
          break;
        case EEventType.onStart:
          if (_this.onStart) {
            _this.onStart(dataResult);
          }
          break;
        case EEventType.onPressDuration:
          if (_this.onPressDuration) {
            _this.onPressDuration(dataResult);
          }
          break;
        case EEventType.onTimeout:
          if (_this.onTimeout) {
            _this.onTimeout(dataResult);
          }
          break;
        case EEventType.onSyncRecord:
          if(_this.onSyncRecord){
            _this.onSyncRecord(dataResult);
          }
          break;
        case EEventType.onLock:
          if(_this.onLock){
            _this.onLock(dataResult);
          }
          break;
        case EEventType.onStamp:
          if (_this.onStamp) {
            _this.onStamp(dataResult);
          }
          break;
        case EEventType.onAlarm:
          if (_this.onAlarm) {
            _this.onAlarm(dataResult);
          }
          break;
        case EEventType.onReset:
        if (_this.onReset) {
          _this.onReset(dataResult);
        }
        break;
      }
    });
  };
  /**
   * 获取类的实例对象，单例模式
   * @param {Object} options 接口参数,key 为必选参数
   */
  static getInstance(options) {
    if(!this.instance) {
      this.instance = new QSSmartSealWX(options);
    }
    
    return this.instance;
  };
  /**
   * 搜索设备
   * @param {*} onDeviceFound 发现设备回调
   */
  startDiscovery(onDeviceFound) {
    var _this = this;
    _this.onDeviceFound = onDeviceFound;
    // 打开蓝牙适配器
    wx.openBluetoothAdapter({
      success(res) {
        wx.startBluetoothDevicesDiscovery({
          allowDuplicatesKey: true,
          powerLevel: "high",
          success(res) {
          }
        });
      },
      fail(msg) {
        if (_this.onDeviceFound) {
          var dataResult = new dataResultInfo();
          dataResult.result = false;
          dataResult.eventType = EEventType.onDeviceFound;
          dataResult.data = null;
          dataResult.message = "请打开蓝牙并在系统设置中允许微信使用蓝牙的权限";
          _this.onDeviceFound(dataResult);
        }
      }
    })
  };
  /**
   * 关闭搜索
   */
  stopDiscovery() {
    var _this = this;
    _this.onDeviceFound = null;
    // 停止搜索
    wx.stopBluetoothDevicesDiscovery({
      success: (res) => { },
    });
  };
  /**
   * 连接设备
   * @param {*} deviceId 设备ID
   * @param {*} onConnectStatus 连接状态变化回调
   */
  connectDevice(deviceId, onConnectStatus) {
    var _this = this;
    _this.deviceId = deviceId;
    _this.onConnectStatus = onConnectStatus;
    BLEUtils.deviceId = deviceId;
    // 连接设备
    wx.createBLEConnection({
      deviceId,
      success() {
        // 延时获取服务
        setTimeout(() => {
          wx.getBLEDeviceServices({
            deviceId,
            success(res) {
              res.services.forEach(service => {
                if (service.uuid == SERVICE_UUID) {
                  wx.getBLEDeviceCharacteristics({
                    deviceId: deviceId,
                    serviceId: service.uuid,
                    success: function (cs) {
                      cs.characteristics.forEach(charater => {
                        if (charater.uuid == NOTIFY_UUID) {
                          // 设置通知特性
                          wx.notifyBLECharacteristicValueChange({
                            characteristicId: charater.uuid,
                            deviceId: deviceId,
                            serviceId: SERVICE_UUID,
                            state: true,
                            success: function () {
  
                            },
                            fail: function () {
  
                            }
                          });
                        }
                      });
                    },
                    fail: function (e) {
  
                    }
                  });
                }
              });
            }, fail: (e) => {
  
            }
          })
        }, 1500);
      }, fail: (e) => {
        if (_this.onConnectStatus) {
          var dataResult = new dataResultInfo();
          dataResult.result = true;
          dataResult.eventType = EEventType.onConnectStatus;
          dataResult.message = "连接失败！";
          dataResult.data = { connectStatus: false };
          _this.onConnectStatus(dataResult);
        }
      }
    });
  };
  /**
   * 断开设备
   */
  disConnectDevice() {
    var _this = this;
    if (_this.onConnectStatus) {
      var dataResult = new dataResultInfo();
      dataResult.result = true;
      dataResult.eventType = EEventType.onConnectStatus;
      dataResult.data = { connectStatus: false };
      _this.onConnectStatus(dataResult);
    }
    _this.onConnectStatus = null;
    // 断开连接
    var deviceId = _this.deviceId;
    if(deviceId){
      wx.closeBLEConnection({
        deviceId: deviceId,
      });
    }
  };

  /**
   * 握手
   * @param {*} onHandshake 回调
   */
  handshake(onHandshake){
    this.onHandshake = onHandshake;
    var buffer = protocol.getHandshake();
    BLEUtils.writeData(this.deviceId, buffer);
  }
  /**
   * 电量
   * @param {*} onBattery 回调
   */
  battery(onBattery){
    this.onBattery = onBattery;
    var buffer = protocol.getBattery();
    BLEUtils.writeData(this.deviceId, buffer);
  }
  /**
   * 启动印章
   * @param {*} count 盖章次数
   * @param {*} time 失效时间
   * @param {*} onStart 回调
   */
  startSeal(count, time, onStart) {
    this.onStart = onStart;
    var buffer = protocol.getStart(count, time);
    BLEUtils.writeData(this.deviceId, buffer);
  };
  /**
   * 查询/设置长按报警时间
   * @param {*} type 操作类型，0：查询，1：设置
   * @param {*} duration 长按报警时长最长为 255 秒，一般设置范围在 3~10 秒，默认 5 秒
   * @param {*} onPressDuration 回调
   */
  pressAlarm(type, duration, onPressDuration) {
    this.onPressDuration = onPressDuration;
    var buffer = protocol.getPressDuration(type, duration);
    BLEUtils.writeData(this.deviceId, buffer);
  };
  /**
   * 查询/设置盖章超时时间
   * @param {*} type 操作类型，0：查询，1：设置
   * @param {*} timeout 盖章超时时间默认值为 60秒，最长为 300 秒，刻度为 30 秒
          例 如设置超时时长为 60 秒，则参数值为 2，2*30=60
          例如设置超时时长为 180 秒，则参数值为 6，6*30=180
   * @param {*} onTimeout 回调
   */
  overTime(type, timeout, onTimeout) {
    this.onTimeout = onTimeout;
    var buffer = protocol.getTimeout(type, timeout);
    BLEUtils.writeData(this.deviceId, buffer);
  };
  /**
   * 同步盖章动作
   * @param {*} onSyncRecord 回调
   */
  syncRecord(onSyncRecord){
    if(onSyncRecord){
      this.onSyncRecord = onSyncRecord;
    }
    var buffer = protocol.getSyncRecord();
    BLEUtils.writeData(this.deviceId, buffer);
  }
  /**
   * 强制锁定印章
   * @param {*} onLock 回调
   */
  lock(onLock){
    this.onLock = onLock;
    var buffer = protocol.getLock();
    BLEUtils.writeData(this.deviceId, buffer);
  }
  /**
   * 重置设备
   * @param {*} onReset 回调
   */
  resetDevice(onReset){
    //RESET onReset
    this.onReset = onReset;
    var buffer = protocol.getReset();
    BLEUtils.writeData(this.deviceId, buffer);
  };
  /**
   * 订阅盖章动作上传事件
   * @param {*} onStamp 回调
   */
  subscribeStampEvent(onStamp) {
    this.onStamp = onStamp;
  };
  /**
   * 订阅报警上传事件
   * @param {*} onAlarm 回调
   */
  subscribeAlarmEvent(onAlarm) {
    this.onAlarm = onAlarm;
  };
};

module.exports = QSSmartSealWX;